MODULE_NAME='Via - VHM Control'
(
    Dev dvVHMServer,
    Dev vdvVHM
)

(***********************************************************)
(***********************************************************)
(*  FILE_LAST_MODIFIED_ON: 04/04/2006  AT: 11:33:16        *)
(***********************************************************)
(* System Type : NetLinx                                   *)
(***********************************************************)
(* REV HISTORY:                                            *)
(***********************************************************)
(*
    $History: $
*)    
//#DEFINE DEBUG = 1
(***********************************************************)
(*               CONSTANT DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_CONSTANT
cnMaxDebugLen = 90

cnMaxScenes = 30

(***********************************************************)
(*              DATA TYPE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_TYPE
Structure _sScene
{
    Char strName[30]
    Char strDesc[30]
    Integer nState
    Integer nType
}

(***********************************************************)
(*               VARIABLE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_VARIABLE
Volatile _sScene sScene[cnMaxScenes]

Volatile Integer nServerPort = 8494
Volatile Integer nServerConnected
Volatile Char strServerData[2000]
Volatile Char strClientBuffer[2000]
Volatile Char strJunk[5]
Volatile SLong sResult

Volatile Char strClientKey[6]

Volatile Char strSceneData[2000]
Volatile Integer nSceneDataChanged
Volatile Integer nSceneColumns
Volatile Integer nNumScenes
Volatile Char strSceneTypes[3][7] = {'blank','heading','button'}
Volatile Long lUpdateTime[1]

Volatile Char strHomeManagerOut[255]

(***********************************************************)
(*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
(***********************************************************)
(* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
(* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)
Define_Call 'DEBUG_OUTPUT' (Integer nLine, Char strData[])
{
    #IF_DEFINED DEBUG
    Char strTempData[200]
    Integer nLoop
    If (Length_String(strData) <= cnMaxDebugLen)
    {
	Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strData"
    }
    Else
    {
	nLoop = 0
	While(Length_String(strData) > cnMaxDebugLen)
	{
	    nLoop++
	    strTempData = Left_String(strData,cnMaxDebugLen)
	    strData = Right_String(strData,Length_String(strData)-cnMaxDebugLen)
	    If (nLoop == 1)
		Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strTempData"
	    Else
		Send_String 0,"'Continued: ',strTempData"
	}
	Send_String 0,"'Continued: ',strData"
    }
    #END_IF
}

Define_Call 'SEND_XML'
{
    Local_Var Char strTemp[2000]
    If (nServerConnected) // Server is connected
    {
    
	strTemp = "'<VHM><key>',strClientKey,'</key>',strSceneData,'</VHM>'"
	Send_String dvVHMServer,"'Status: HTTP/1.1 200 OK',13,10,'Content-Length: ',Itoa(Length_String(strTemp)),13,10,13,10,strTemp,13,10"
	Call 'DEBUG_OUTPUT'(__LINE__,"'Status: HTTP/1.1 200 OK',13,10,'Content-Length: ',Itoa(Length_String(strTemp)),13,10,13,10,strTemp,13,10")
	nSceneDataChanged = 0
	IP_Server_Close(dvVHMServer.Port)
	Wait 2 'SERVER_WAIT'
	{
	    sResult = Ip_Server_Open (dvVHMServer.Port, nServerPort, IP_TCP)
	    Call 'DEBUG_OUTPUT'(__LINE__,"'Ip_Server_Open sResult:',Itoa(sResult)")
	}
    }
    Else
    {
	Call 'DEBUG_OUTPUT' (__LINE__,"'SEND_XML: Server not connected.'")
    }
}

Define_Call 'CREATE_SCENEDATA'
{
    Local_Var Integer nI
    
    strSceneData = "'<propertyControl><presets><cols>',Itoa(nSceneColumns),'</cols>'"
    For (nI = 1; nI <= nNumScenes; nI++)
    {
	strSceneData = "strSceneData,'<preset>'"
	switch (sScene[nI].nType)
	{
	    Case 0:
	    {
		strSceneData = "strSceneData,'<type>',strSceneTypes[sScene[nI].nType+1],'</type>'"
	    }
	    Case 1:
	    {
		strSceneData = "strSceneData,'<name>',sScene[nI].strName,'</name><type>',strSceneTypes[sScene[nI].nType+1],'</type><desc>',sScene[nI].strDesc,'</desc>'"
	    }
	    Case 2:
	    {
		strSceneData = "strSceneData,'<id>',Itoa(nI),'</id><name>',sScene[nI].strName,'</name><type>',strSceneTypes[sScene[nI].nType+1],'</type><state>',Itoa(sScene[nI].nState),'</state><desc>',sScene[nI].strDesc,'</desc>'"
	    }
	}
	strSceneData = "strSceneData,'</preset>'"
    }
    strSceneData = "strSceneData,'</presets></propertyControl>'"
}

Define_Call 'UPDATE'
{
    If (nServerConnected) // Server is connected
    {
	Send_String dvVHMServer,"'Status: HTTP/1.1 200 OK',13,10,'Content-Length: 1',13,10,Itoa(nSceneDataChanged)"
	Call 'DEBUG_OUTPUT'(__LINE__,"'Status: HTTP/1.1 200 OK',13,10,'Content-Length: 1',13,10,Itoa(nSceneDataChanged)")
	nSceneDataChanged = 0
	IP_Server_Close(dvVHMServer.Port)
	Wait 2 'SERVER_WAIT'
	{
	    sResult = Ip_Server_Open (dvVHMServer.Port, nServerPort, IP_TCP)
	    Call 'DEBUG_OUTPUT'(__LINE__,"'Ip_Server_Open sResult:',Itoa(sResult)")
	}
    }
    Else
    {
	Call 'DEBUG_OUTPUT' (__LINE__,"'UPDATE: Server not connected.'")
    }
}	

Define_Function Char[255] fnURLEncode(Char szText[])
{
    Local_Var Integer nI, nByte
    Char szChar[3], szEncodedText[256]
    For(nI=1; nI <= Length_String(szText); nI++)
    {
	Switch(szText[nI])		
	{			/* Reserved characters */
	    case 36: szChar = '%24' // Dollar  "$"
	    case 38: szChar = '%26' // Ampersand  "&"
	    case 43: szChar = '%2B' // Plus  "+"			
	    case 44: szChar = '%2C' // Comma  ","			
	    case 47: szChar = '%2F' // Forward slash/Virgule  "/"			
	    case 58: szChar = '%3A' // Colon  ":"			
	    case 59: szChar = '%3B' // Semi-colon  ";"			
	    case 61: szChar = '%3D' // Equals  "="			
	    case 63: szChar = '%3F' // Question mark  "?"			
	    case 64: szChar = '%40' // 'At' symbol  "@"			
	    /* Unsafe characters */			
	    case 32:  szChar = '%20' // Space			
	    case 34:  szChar = '%22' // Quotation marks			
	    case 60:  szChar = '%3C' // 'Less Than' symbol  "<"			
	    case 62:  szChar = '%3E' // 'Greater Than' symbol  ">"			
	    case 35:  szChar = '%23' // 'Pound' character  "#"			
	    case 37:  szChar = '%25' // Percent character  '%"			
	    case 123: szChar = '%7B' // Left Curly Brace  "{"			
	    case 125: szChar = '%7D' // Right Curly Brace  "}"			
	    case 124: szChar = '%7C' // Vertical Bar/Pipe  "|"			
	    case 92:  szChar = '%5C' // Backslash  "\"			
	    case 94:  szChar = '%5E' // Caret  "^"			
	    case 126: szChar = '%7E' // Tilde  "~"			
	    case 91:  szChar = '%5B' // Left Square Bracket  "["			
	    case 93:  szChar = '%5D' // Right Square Bracket  "]"			
	    case 96:  szChar = '%60' // Grave Accent  "`"			
	    default: szChar = Mid_String(szText,nI,1)
	}
	szEncodedText = "szEncodedText,szChar"
    }
    return(szEncodedText)
}

Define_Function CHAR[100] GET_IP_ERROR (LONG lERR)
{
    Switch (lERR)
    {
	Case 0:
	    Return "";
	Case 2:
	    Return "'IP ERROR (',ITOA(lERR),'): General Failure (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 4:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown host (IP_CLIENT_OPEN)'";
	Case 6:
	    Return "'IP ERROR (',ITOA(lERR),'): connection refused (IP_CLIENT_OPEN)'";
	Case 7:
	    Return "'IP ERROR (',ITOA(lERR),'): connection timed out (IP_CLIENT_OPEN)'";
	Case 8:
	    Return "'IP ERROR (',ITOA(lERR),'): unknown connection error (IP_CLIENT_OPEN)'";
	Case 9:
	    Return "'IP ERROR (',ITOA(lERR),'): Already closed (IP_CLIENT_CLOSE/IP_SERVER_CLOSE)'";
	Case 10:
	    Return "'IP ERROR (',ITOA(lERR),'): Binding error (IP_SERVER_OPEN)'";
	Case 11:
	    Return "'IP ERROR (',ITOA(lERR),'): Listening error (IP_SERVER_OPEN)'";
	Case 14:
	    Return "'IP ERROR (',ITOA(lERR),'): local port already used (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Case 15:
	    Return "'IP ERROR (',ITOA(lERR),'): UDP socket already listening (IP_SERVER_OPEN)'";
	Case 16:
	    Return "'IP ERROR (',ITOA(lERR),'): too many open sockets (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
	Default:
	    Return "'IP ERROR (',ITOA(lERR),'): Unknown'";
    }
}

(***********************************************************)
(*                STARTUP CODE GOES BELOW                  *)
(***********************************************************)
DEFINE_START

(***********************************************************)
(*                THE EVENTS GO BELOW                      *)
(***********************************************************)
DEFINE_EVENT

Data_Event[dvVHMServer]
{
    Online:
    {
	Cancel_Wait 'SERVER_WAIT'
	strServerData = ''
	nServerConnected = 1
	Send_String dvVHMServer,"'OK',13,10,13,10"
	Call 'DEBUG_OUTPUT' (__LINE__,'Online')
    }
    Offline:
    {
	Cancel_Wait 'SERVER_WAIT'
	sResult = IP_SERVER_OPEN (dvVHMServer.Port, nServerPort, IP_TCP)
	Call 'DEBUG_OUTPUT'(__LINE__,"'Ip_Server_Open sResult:',Itoa(sResult)")
	strServerData = ''
	nServerConnected = 0
	Call 'DEBUG_OUTPUT' (__LINE__,'Offline')
    }
    OnError:
    {
	strServerData = ''
	Call 'DEBUG_OUTPUT' (__LINE__, "GET_IP_ERROR(Data.Number)");
	//If (Data.Number != 17)
	//{
	//    nServerConnected = 0
	//}
    }
    String:
    {
	Local_Var Integer nBtn, nUserID
	strServerData = "strServerData,Data.Text"
	Call 'DEBUG_OUTPUT' (__LINE__,"'strServerData:',strServerData")
	If (Find_String(strServerData,'updateMe',1))
	{
	    Call 'UPDATE'
	    strJunk = Remove_String(strServerData,'updateMe',1)
	}
	Else If (Find_String(strServerData,'propertyControl&action=init',1))
	{
	    Call 'CREATE_SCENEDATA'
	    Call 'SEND_XML'
	    strJunk = Remove_String(strServerData,'propertyControl&action=init',1)
	}
	Else If (Find_String(strServerData,'propertyControl&',1))
	{
	    strJunk = Remove_String(strServerData,'propertyControl&',1)
	    If (Find_String(strServerData,'action=press&userID=',1))
	    {
		strJunk = Remove_String(strServerData,'action=press&userID=',1)	
		Call 'DEBUG_OUTPUT'(__LINE__,'action=press&userID= received')
		nUserID = Atoi(strServerData)
		strJunk = Remove_String(strServerData,'&buttonID=',1)
		nBtn = Atoi(strServerData)
		If (nBtn > 0 && nBtn <= nNumScenes)
		{
		    Do_Push(vdvVHM,nBtn)
		    IP_Server_Close(dvVHMServer.Port)
		    Wait 2 'SERVER_WAIT'
		    {
			sResult = Ip_Server_Open (dvVHMServer.Port, nServerPort, IP_TCP)
			Call 'DEBUG_OUTPUT'(__LINE__,"'Ip_Server_Open sResult:',Itoa(sResult)")
		    }
		}
	    }
	}
    }
}

Data_Event[vdvVHM]
{
    Command:
    {
	Local_Var Char strCommand[20]
	Local_Var Integer nI
	// Format: SERVERPORT=nServerPort
	Call 'DEBUG_OUTPUT' (__LINE__,"'vdvVHM Command Data.Text=',Data.Text");
	If (Find_String(Data.Text,'=',1))
	{
	    strCommand = Upper_String(Remove_String(Data.Text,'=',1))
	    If (strCommand == 'SERVERPORT=')
	    {
		nServerPort = Atoi(Data.Text)
		sResult = Ip_Server_Open (dvVHMServer.Port, nServerPort, IP_TCP)
	    }
	    If (strCommand == 'CLIENTKEY=')
	    {
		strClientKey = Data.Text
	    }
	    If (strCommand == 'COLUMNS=')
	    {
		nSceneColumns = Atoi(Data.Text)
	    }
	    Else If (strCommand == 'INITIALIZE')
	    {
		nSceneDataChanged = 1
		Call 'CREATE_SCENEDATA'
		Call 'DEBUG_OUTPUT' (__LINE__,"'INITIALIZE Complete'")
	    }
	    Else If (Find_String(strCommand,'SCENENAME',1))
	    {
		strJunk = Remove_String(strCommand,'SCENENAME',1)
		nI = Atoi(strCommand)
		sScene[nI].strName = Data.Text
		If (nNumScenes < nI)
		    nNumScenes = nI
	    }
	    Else If (Find_String(strCommand,'SCENEDESC',1))
	    {
		strJunk = Remove_String(strCommand,'SCENEDESC',1)
		sScene[Atoi(strCommand)].strDesc = Data.Text
	    }
	    Else If (Find_String(strCommand,'SCENETYPE',1))
	    {
		strJunk = Remove_String(strCommand,'SCENETYPE',1)
		sScene[Atoi(strCommand)].nType = Atoi(Data.Text)
	    }
	}
    }
}

Channel_Event[vdvVHM,0]
{
    On:
    {
	sScene[Channel.Channel].nState = 1
	nSceneDataChanged = 1
    }
    Off:
    {
	sScene[Channel.Channel].nState = 0
	nSceneDataChanged = 1
    }
}

(***********************************************************)
(*            THE ACTUAL PROGRAM GOES BELOW                *)
(***********************************************************)
DEFINE_PROGRAM

(***********************************************************)
(*                     END OF PROGRAM                      *)
(*        DO NOT PUT ANY CODE BELOW THIS COMMENT           *)
(***********************************************************)